﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Runtime.Serialization;
using Airport.AirportDataTypes;

namespace Airport
{
    [DataContract(IsReference = true)]
    [KnownType(typeof(Mitigation))]
    public class Incursion
    {
        private int _incursionId;
        private List<Plane> _planesInvolved;
        private RouteNode _incursionRouteNode;
        private List<Route> _incursionRoutes;
        private string _intersection;
        private List<Mitigation> _mitigations;

        public Incursion()
            : this(-1, new List<Plane>(), new RouteNode(), new List<Route>())
        {
        }

        public Incursion(int incursionId, List<Plane> planesInvolved, RouteNode incursionRouteNode, List<Route> incursionRoutes)
        {
            _incursionId = incursionId;
            _planesInvolved = planesInvolved;
            _incursionRouteNode = incursionRouteNode;
            _incursionRoutes = incursionRoutes;
            _intersection = "";
            _mitigations = new List<Mitigation>();
        }

        public Incursion(int incursionId, Plane[] planesInvolved, RouteNode incursionRouteNode, Route[] incursionRoutes)
        {
            _incursionId = incursionId;
            _planesInvolved = new List<Plane>(planesInvolved);
            _incursionRouteNode = incursionRouteNode;
            _incursionRoutes = new List<Route>(incursionRoutes);
            _intersection = "";
            _mitigations = new List<Mitigation>();
        }

        [DataMember]
        public int IncusionId
        {
            get
            {
                return _incursionId;
            }

            set
            {
                _incursionId = value;
            }
        }

        /// <summary>
        /// A List of the Planes that will potentially have an incursion.
        /// </summary>
        [DataMember]
        public List<Plane> PlanesInvolved
        {
            get
            {               
                return _planesInvolved;
            }

            set
            {
                _planesInvolved = value;
            }
        }

        /// <summary>
        /// The RouteNode at which the incursion is predicted to take place.
        /// </summary>
        [DataMember]
        public RouteNode IncursionRouteNode
        {
            get
            {
                return _incursionRouteNode;
            }

            set
            {
                _incursionRouteNode = value;
            }
        }

        [DataMember]
        public List<Route> IncursionRoutes
        {
            get
            {
                return _incursionRoutes;
            }

            set
            {
                _incursionRoutes = value;
            }
        }

        [DataMember]
        public string Intersection
        {
            get
            {
                return _intersection;
            }

            set
            {
                _intersection = value;
            }
        }
       
        [DataMember]
        public List<Mitigation> Mitigations
        {
            get
            {
                return _mitigations;
            }

            set
            {
                _mitigations = value;
            }
        }

        public override bool Equals(Object obj)
        {
            if (obj.GetType() != typeof(Incursion))
            {
                return false;
            }

            if (_incursionRouteNode != ((Incursion)obj)._incursionRouteNode)
            {
                return false;
            }

            if (_planesInvolved.Count != ((Incursion)obj)._planesInvolved.Count)
            {
                return false;
            }

            foreach (Plane plane in ((Incursion)obj)._planesInvolved)
            {
                if (!_planesInvolved.Contains(plane))
                {
                    return false;
                }
            }

            return true;            
        }

        public static bool operator ==(Incursion a, Incursion b)
        {
            if (System.Object.ReferenceEquals(a, b))
            {
                return true;
            }

            if (((object)a == null) || ((object)b == null))
            {
                return false;
            }

            if (a._incursionRouteNode != b._incursionRouteNode)
            {
                return false;
            }

            if (a._planesInvolved.Count != b._planesInvolved.Count)
            {
                return false;
            }

            foreach (Plane plane in a._planesInvolved)
            {
                if (!b._planesInvolved.Contains(plane))
                {
                    return false;
                }
            }

            return true;
        }

        public static bool operator !=(Incursion a, Incursion b)
        {
            return !(a == b);
        }
        
    }

    public class IncursionPlanesInvolvedEqualityComparer : EqualityComparer<Incursion>
    {
        public override bool Equals(Incursion a, Incursion b)
        {
            //Check whether the compared objects reference the same data.
            if (Object.ReferenceEquals(a, b)) return true;

            //Check whether any of the compared objects is null.
            if (Object.ReferenceEquals(a, null) || Object.ReferenceEquals(b, null))
                return false;

            if (a.PlanesInvolved.Count != b.PlanesInvolved.Count)
            {
                return false;
            }

            foreach (Plane plane in a.PlanesInvolved)
            {
                if (!b.PlanesInvolved.Contains(plane))
                {
                    return false;
                }
            }

            return true;
        }

        public override int GetHashCode(Incursion obj)
        {
            string planeIdString = "";
            List<Plane> planesInvolved = obj.PlanesInvolved;

            //Check whether the object is null
            if (Object.ReferenceEquals(obj, null)) return 0;

            // Overhead should not be a problem, n should be relatively small
            planesInvolved.Sort(new PlaneComparer());

            foreach (Plane plane in planesInvolved)
            {
                planeIdString += plane.PlaneId.ToString();
            }

            return planeIdString.GetHashCode();
        }
    }
}